import JsSIP from 'jssip';
import mitt from 'mitt';
import { resolveSipUrl, resolveWebsocketUrl, resolveContactSipUrl } from './utils';
import {
  VERSION,
  SIP_CONNECTOR_DEFAULT_CONFIG,
  AUDIO_CONSTRAINTS,
  VIDEO_CONSTRAINTS,
} from './config';
import SIPError from './error';

const getURI = conf => {
  return resolveSipUrl(conf);
};
const getContactURI = conf => {
  return resolveContactSipUrl(conf);
};
const getWebsocketURL = conf => {
  return resolveWebsocketUrl(conf);
};

/** 创建音频dom */
const createAudioDom = () => {
  const audioDom = document.createElement('audio');
  audioDom.controls = false;
  audioDom.style.display = 'none';

  return audioDom;
};

class JsSIPSDK {
  /** 单例实例 */
  static #_instance;
  /** eventbus对象 */
  #_emitter;
  #_id;
  constructor() {
    if (JsSIPSDK.#_instance) {
      return JsSIPSDK.#_instance;
    }
    JsSIPSDK.#_instance = this;
    this.#_emitter = mitt();
    this.version = VERSION;

    /** 用户代理 */
    this.ua = null;
    /** 会话实例 */
    this.currentSession = null;
    /** 通话类型 */
    this.callType = 'audio';

    /** 电话方向: local remote */
    this.originator = null;
    /** 本地号码 */
    this.localNumber = null;
    /** 远程号码 */
    this.remoteNumber = null;

    /** 本地音频dom */
    this.localAudio = null;
    /** 本地视频dom */
    this.localVideo = null;
    /** 远程视频dom */
    this.remoteVideo = null;
    /** 呼入铃声 */
    this.incomingRingtone = null;
    /** 呼出铃声 */
    this.outgoingRingtone = null;
    /** 本地音视频流 */
    this.localAudioStream = null;
    /** 本地视频流 */
    this.localVideoStream = null;
    /** 桌面共享流 */
    this.desktopMediaStream = null;

    this.#_initAudioDom();
  }

  /** 初始化音频dom */
  #_initAudioDom() {
    if (this.#_id) return;
    this.#_id = '_sip_' + Math.random().toString(36).substring(2, 10);
    this.localAudio = createAudioDom();
    this.incomingRingtone = createAudioDom();
    this.outgoingRingtone = createAudioDom();

    const div = document.createElement('div');
    div.id = this.#_id;
    div.style.cssText = 'width: 0; height: 0';
    div.appendChild(this.localAudio);
    div.appendChild(this.incomingRingtone);
    div.appendChild(this.outgoingRingtone);
    document.body.appendChild(div);
  }

  /** 获取音频流 */
  async #_getLocalAudioStream() {
    if (!this.localAudioStream) {
      this.localAudioStream = await navigator.mediaDevices.getUserMedia({
        audio: AUDIO_CONSTRAINTS,
      });
    }
    return this.localAudioStream;
  }

  /** 获取视频流 */
  async #_getLocalVideoStream() {
    if (!this.localVideoStream) {
      this.localVideoStream = await navigator.mediaDevices.getUserMedia({
        audio: AUDIO_CONSTRAINTS,
        video: VIDEO_CONSTRAINTS,
      });
    }
    return this.localVideoStream;
  }

  /** 切换音频播放状态 */
  #_toggleAudio(audio, isPlay) {
    if (isPlay) {
      audio?.paused && audio?.play();
    } else {
      !audio?.paused && (audio.srcObject || audio.src) && audio?.pause();
    }
  }

  /** 注册相关事件 */
  #_setupRegistrationListener() {
    /** 注册成功 */
    this.ua.on('registered', e => {
      this.#_emitter.emit('registered', e);
    });
    /** 取消注册 */
    this.ua.on('unregistered', e => {
      this.#_emitter.emit('unregistered', e);
    });
    /** 注册失败 */
    this.ua.on('registrationFailed', e => {
      this.#_emitter.emit('registrationFailed', e);
    });
    /** 注册过期 */
    this.ua.on('registrationExpiring', e => {
      this.#_emitter.emit('registrationExpiring', e);
    });
    // ws的连接事件，不常用
    // this.ua.on('connecting', e => {
    //   console.log('connecting', e);
    // });
    // this.ua.on('connected', e => {
    //   console.log('connected', e);
    // });
    // this.ua.on('disconnected', e => {
    //   console.log('disconnected', e);
    // });
    // 其他事件，如消息等，不常用
    // this.ua.on('newMessage', e => {
    //   console.log('newMessage', e);
    // });
    // this.ua.on('newOptions', e => {
    //   console.log('newOptions', e);
    // });
    // this.ua.on('sipEvent', e => {
    //   console.log('sipEvent', e);
    // });
  }

  /** 会话相关事件 */
  #_setupSessionEvents(e) {
    const { originator, request, session } = e;
    this.currentSession = session;
    this.originator = originator;

    [
      'peerconnection',
      'connecting',
      'sending',
      'progress',
      'accepted',
      'confirmed',
      'ended',
      'failed',
      'newDTMF',
      'newInfo',
      'hold',
      'unhold',
      'muted',
      'unmuted',
      'reinvite',
      'update',
      'refer',
      'replaces',
      'sdp',
      'icecandidate',
      'getusermediafailed',
      'peerconnection:createofferfailed',
      'peerconnection:createanswerfailed',
      'peerconnection:setlocaldescriptionfailed',
      'peerconnection:setremotedescriptionfailed',
    ].forEach(eventName => {
      this.currentSession.on(eventName, e => {
        // console.log('所有事件--> ', eventName, e);
      });
    });

    this.currentSession.on('connecting', e => {
      if (originator == 'local') {
        this.currentSession.connection.addEventListener('track', event => {
          // 拨打电话，取消保持
          this.#_trackHandle(event.track, event.streams[0]);
        });
      }
    });
    this.currentSession.on('peerconnection', e => {
      if (originator == 'remote') {
        this.currentSession.connection.addEventListener('track', event => {
          // 接听电话，取消保持
          this.#_trackHandle(event.track, event.streams[0]);
        });
      }
    });
    this.currentSession.on('progress', e => {
      this.callType = request.body?.includes('m=video') ? 'video' : 'audio';
      if (originator === 'remote') {
        this.remoteNumber = request.from.uri.user;
        this.#_toggleAudio(this.incomingRingtone, true);
      } else {
        this.#_toggleAudio(this.outgoingRingtone, true);
      }
      this.#_emitter.emit('ringing', {
        session: this.currentSession,
        callType: this.callType,
        originator: this.originator,
        remoteNumber: this.remoteNumber,
        localNumber: this.localNumber,
      });
    });
    this.currentSession.on('accepted', e => {
      this.callType = request.body?.includes('m=video') ? 'video' : 'audio';
      if (originator === 'remote') {
        this.remoteNumber = request.from.uri.user;
        this.#_toggleAudio(this.incomingRingtone, false);
      } else {
        this.#_toggleAudio(this.outgoingRingtone, false);
      }
      this.#_emitter.emit('answer', {
        session: this.currentSession,
        callType: this.callType,
        originator: this.originator,
        remoteNumber: this.remoteNumber,
        localNumber: this.localNumber,
      });
    });
    // 处理接听逻辑
    this.currentSession.on('confirmed', e => {
      if (this.callType === 'audio') {
        this.#_playRemoteAudio();
      } else {
        this.#_playLocalVideo();
        this.#_playRemoteVideo();
      }
    });
    this.currentSession.on('ended', e => {
      this.#_handleEnded();
      if (originator === 'remote') {
        this.#_toggleAudio(this.incomingRingtone, false);
      } else {
        this.#_toggleAudio(this.outgoingRingtone, false);
      }
      this.#_emitter.emit('hangup');
    });
    this.currentSession.on('failed', e => {
      this.#_handleEnded();
      if (originator === 'remote') {
        this.#_toggleAudio(this.incomingRingtone, false);
      } else {
        this.#_toggleAudio(this.outgoingRingtone, false);
      }
      this.#_emitter.emit('hangup');
    });
  }

  #_setupUserAgentListener() {
    this.#_setupRegistrationListener();
    this.ua.on('newRTCSession', e => this.#_setupSessionEvents(e));
  }

  /** 轨道处理，主要用于保持后恢复 */
  #_trackHandle(track, stream) {
    if (track.kind === 'audio') {
      // 使用 audio 元素播放音频轨道
      this.localAudio.srcObject = stream;
      this.localAudio.play();
    }
  }

  /** 播放本地视频 */
  async #_playLocalVideo() {
    const mediaStream = await this.#_getLocalVideoStream();
    if (mediaStream) {
      this.localVideo.srcObject = mediaStream;
      this.localVideo.muted = true; // 设置为静音，不播放本地声音
      this.localVideo.play();
    }
  }

  /** 播放远程音频 */
  #_playRemoteAudio() {
    const { connection } = this.currentSession;
    if (!connection || !connection.getRemoteStreams().length) {
      return;
    }
    this.localAudio.srcObject = connection.getRemoteStreams()[0];
    this.localAudio.play();
  }

  /** 播放远程视频 */
  #_playRemoteVideo() {
    const { connection } = this.currentSession;
    if (!connection || !connection.getRemoteStreams().length) {
      return;
    }
    this.remoteVideo.srcObject = connection.getRemoteStreams()[0];
    this.remoteVideo.play();
  }

  /** 共享桌面切换为本地视频 */
  async #_switchLocalCamera() {
    if (!this.currentSession) return;
    let peerConnection = this.currentSession.connection;
    const stream = await this.#_getLocalVideoStream();
    // 替换本地流
    this.localVideo.srcObject = stream;
    this.localVideoStream = stream;
    // 替换远程轨道
    peerConnection.getSenders().forEach(sender => {
      if (sender.track && sender.track.kind === 'video') {
        sender.replaceTrack(stream.getVideoTracks()[0]);
      }
    });
    this.#_emitter.emit('endDesktopSharing');
  }

  /** 清除流 */
  #_clearStream(stream) {
    if (stream) {
      stream.getTracks().forEach(track => track.stop());
    }
  }

  /** 清除媒体 */
  #_clearMedia(media) {
    if (!media) return;
    media.pause();
    if (media.srcObject) {
      this.#_clearStream(media.srcObject);
    }
    media.srcObject = null;
  }

  /** 清除所有媒体流 */
  #_clearAllMedia() {
    this.#_clearMedia(this.localAudio);
    this.#_clearMedia(this.localVideo);
    this.#_clearMedia(this.remoteVideo);

    this.#_clearStream(this.localAudioStream);
    this.#_clearStream(this.localVideoStream);
    this.localAudioStream = null;
    this.localVideoStream = null;
  }

  #_handleEnded() {
    this.#_clearAllMedia();
    this.currentSession = null;
    this.callType = 'audio';
    this.remoteNumber = null;
  }

  /** 检查麦克风 */
  async checkMic() {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      stream.getTracks().forEach(track => track.stop());
    } catch (error) {
      throw new SIPError(SIPError.ErrorCode.mic, error);
    }
  }

  /** 检查摄像头 */
  async checkCamera() {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ video: true });
      stream.getTracks().forEach(track => track.stop());
    } catch (error) {
      throw new SIPError(SIPError.ErrorCode.camera, error);
    }
  }

  /** 设置视频元素 */
  setVideoDom(data) {
    if (!data) throw SIPError(SIPError.ErrorCode.params);
    if (!data.localVideo && !data.remoteVideo) throw SIPError(SIPError.ErrorCode.params);
    const { localVideo, remoteVideo } = data;
    this.localVideo =
      typeof localVideo === 'string' ? document.querySelector(localVideo) : localVideo;
    this.remoteVideo =
      typeof remoteVideo === 'string' ? document.querySelector(remoteVideo) : remoteVideo;
  }

  /** 设置铃声 */
  setRingtone({ incoming, outgoing }) {
    if (!incoming || !outgoing) throw SIPError(SIPError.ErrorCode.params);
    this.incomingRingtone.src = incoming;
    this.outgoingRingtone.src = outgoing;
  }

  /** TODO 初始化 SDK，需确认传入还是后端给值 */
  async init(config) {
    if (this.ua) {
      this.destroy();
    }
    JsSIP.debug.disable('JsSIP:*');
    // JsSIP.debug.enable('JsSIP:*');

    await Promise.all([this.checkMic(), this.checkCamera()]);

    // sip配置信息
    const sipConfig = {
      ...SIP_CONNECTOR_DEFAULT_CONFIG,
      ...config,
    };
    this.config = sipConfig;
    this.localNumber = sipConfig.sipName;

    const socket = new JsSIP.WebSocketInterface(getWebsocketURL(sipConfig));
    this.ua = new JsSIP.UA({
      sockets: [socket],
      uri: getURI(sipConfig),
      contact_uri: getContactURI(sipConfig),
      password: sipConfig.password,
      register: sipConfig.register,
      register_expires: 3600,
      display_name: sipConfig.display_name || sipConfig.sipName,
    });
    this.#_setupUserAgentListener();
    this.ua.start();
  }

  /** 发起呼叫 */
  async call(target, callType = 'audio', otherConfig) {
    if (this.ua) {
      this.remoteNumber = target;
      this.callType = callType;
      if (callType !== 'video' && callType !== 'audio') {
        throw new SIPError(SIPError.ErrorCode.params);
      }

      let mediaStream = null;
      if (callType === 'video') {
        mediaStream = await this.#_getLocalVideoStream();
      } else {
        mediaStream = await this.#_getLocalAudioStream();
      }

      // // 额外参数
      // const otherConfig = {};
      // // 外显号码
      // if (channelNumber.value) {
      // 	otherConfig['extraHeaders'] = [`X-Channel-NUMBER: ${channelNumber.value}`];
      // }
      this.currentSession = this.ua.call(String(target), {
        mediaConstraints: { audio: true, video: callType === 'video' },
        sessionTimersExpires: 1800,
        mediaStream,
        ...otherConfig,
      });
    }
  }

  /** 接听 */
  async answerCall() {
    if (this.currentSession) {
      let mediaStream = null;
      if (this.callType === 'video') {
        mediaStream = await this.#_getLocalVideoStream();
      } else {
        mediaStream = await this.#_getLocalAudioStream();
      }

      this.currentSession.answer({
        mediaConstraints: { audio: true, video: this.callType === 'video' },
        sessionTimersExpires: 1800,
        mediaStream,
      });
    }
  }

  /** 切换为语音，视频模式下 */
  async switchToAudio() {
    if (this.callType !== 'video') return;
    const streams = await this.#_getLocalVideoStream();
    streams?.getVideoTracks().forEach(track => {
      track.enabled = false;
    });
    // this.ua.sendMessage(localDestNumber, 'video-disabled');
  }

  /** 切换为视频，视频模式下 */
  async switchToVideo() {
    if (this.callType !== 'video') return;
    const streams = await this.#_getLocalVideoStream();
    streams?.getVideoTracks().forEach(track => {
      track.enabled = true;
    });
    // this.ua.sendMessage(localDestNumber, 'video-enabled');
  }

  /** 挂断 */
  hangup() {
    if (this.currentSession) {
      this.currentSession.terminate();
    }
  }

  /** 静音 */
  mute() {
    if (this.currentSession) {
      this.currentSession.mute({ audio: true, video: false });
      this.#_emitter.emit('muted');
    }
  }

  /** 取消静音 */
  unmute() {
    if (this.currentSession) {
      this.currentSession.unmute({ audio: true, video: false });
      this.#_emitter.emit('unmuted');
    }
  }

  /** 保持 */
  hold() {
    if (this.currentSession) {
      // useUpdate:Boolean Send UPDATE instead of re-INVITE
      this.currentSession.hold({ useUpdate: false });
      this.#_emitter.emit('hold');
    }
  }

  /** 取消保持 */
  unhold() {
    if (this.currentSession) {
      this.currentSession.unhold({ useUpdate: false });
      this.#_emitter.emit('unhold');
    }
  }

  /** TODO 转接，可能是调接口 */
  async transfer(transferPhone) {
    // if (this.currentSession) {
    // 	this.currentSession.sendDTMF(transferPhone);
    // }
  }

  /** 发送DTMF指令 */
  sendDTMF(btn) {
    this.currentSession?.sendDTMF(btn);
  }

  /** 开始屏幕共享 */
  async desktopSharing() {
    if (!this.currentSession) return;
    let peerConnection = this.currentSession.connection;
    const disStream = await navigator.mediaDevices.getDisplayMedia({ video: true });

    this.desktopMediaStream = disStream;

    const videoTrack = disStream.getVideoTracks()[0];
    peerConnection.getSenders().forEach(sender => {
      if (sender.track.kind === 'video') {
        sender.replaceTrack(videoTrack);
      }
    });
    this.localVideo.srcObject = disStream;
    this.#_emitter.emit('desktopSharing');

    // 点浏览器的停止共享会触发
    videoTrack.addEventListener('ended', e => {
      // '屏幕共享结束, 准备切换为本地摄像头
      this.#_switchLocalCamera();
    });
    // 监听屏幕共享结束方案2，浏览器的停止和手动停止都会触发
    // disStream.oninactive = e => {
    //   console.log('oninactive', e);
    // };
  }

  /** 结束屏幕共享 */
  async endDesktopSharing() {
    if (!this.desktopMediaStream) {
      return;
    }
    this.#_clearStream(this.desktopMediaStream);
    this.desktopMediaStream = null;
    this.#_switchLocalCamera();
  }

  register() {
    this.ua?.register();
  }

  unregister() {
    this.ua?.unregister();
  }
  /** 连接服务，如果ua配置中register为true时，自动注册 */
  start() {
    this.ua?.start();
  }
  /** 断开连接，不会取消注册 */
  stop() {
    this.ua?.stop();
  }

  /** 销毁 */
  destroy() {
    this.#_clearAllMedia();
    this.currentSession = null;
    this.ua?.unregister();
    this.ua?.stop();
    this.ua = null;
    JsSIPSDK.#_instance = null;
  }

  on(type, callback) {
    this.#_emitter.on(type, callback);
  }
  off(type, callback) {
    this.#_emitter.off(type, callback);
  }
}

export default JsSIPSDK;
